import React, { ReactNode, useCallback } from 'react'
import * as auth from 'auth-provider'
import { http } from 'utils/http'
import { useMount } from 'utils'
import { useAsync } from 'utils/use-async'
import { FullPageErrorFallback, FullPageLoading } from 'components/lib'
import { User } from 'types/user'
import { useQueryClient } from 'react-query'

interface AuthForm {
  username: string
  password: string
}

// 获取用户信息
const bootstrapUser = async () => {
  let user = null
  const token = auth.getToken()
  if (token) {
    const data = await http('me', { token }) // 获取用户信息
    user = data.user
  }
  return user
}
type IContext = {
  user: User | null
  register: (form: AuthForm) => Promise<void>
  login: (form: AuthForm) => Promise<void>
  logout: () => Promise<void>
}
const AuthContext = React.createContext<IContext | undefined>(undefined)
AuthContext.displayName = 'AuthContext' // 给context起一个名字，方便调试

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const {
    data: user,
    error,
    isLoading, // 加载中
    isIdle, // 空闲
    isError, // 错误
    run,
    setData: setUser
  } = useAsync<User | null>()
  const queryClient = useQueryClient()

  const login = (form: AuthForm) => auth.login(form).then(setUser) // 登录
  const register = (form: AuthForm) => auth.register(form).then(setUser) // 注册
  // 退出登录
  const logout = () =>
    auth.logout().then(() => {
      setUser(null)
      queryClient.clear()
    })
  // 每次页面刷新时，会自动调用bootstrapUser函数，获取用户信息
  useMount(
    useCallback(() => {
      run(bootstrapUser())
    }, [])
  )
  if (isIdle || isLoading) {
    return <FullPageLoading />
  }
  if (isError) {
    return <FullPageErrorFallback error={error} />
  }
  return (
    <AuthContext.Provider
      children={children}
      value={{
        user, // 用户信息
        login, // 登录
        register, // 注册
        logout // 退出登录
      }}
    />
  )
}
// 二次封装
export const useAuth = () => {
  const context = React.useContext(AuthContext)
  if (!context) throw new Error('error context')
  return context
}
